package com.luo.d3s.anti.mvc.ec.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;

/**
 * RabbitMq - 基础配置
 * <ol>
 *     <li>queue</li>
 *     <li>exchange</li>
 *     <li>binding</li>
 * </ol>
 *
 * @author luohq
 * @date 2019/4/11
 */
public class RabbitBaseConfig implements BeanFactoryAware {

    private static final Logger log = LoggerFactory.getLogger(RabbitBaseConfig.class);

    /**
     * Spring bean工厂
     */
    private ConfigurableBeanFactory configurableBeanFactory;

    /**
     * 领域事件名称（对应DomainEvent子类名称，如OrderPaidEvent）
     */
    private String eventName;

    /**
     * exchange配置
     */
    private ExchangeProps exchange;
    /**
     * queue配置
     */
    private QueueProps queue;


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        Assert.state(beanFactory instanceof ConfigurableBeanFactory, "RabbitBaseConfig wrong beanFactory type");
        this.configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
    }


    @PostConstruct
    public void configure() {
        log.info("RabbitMq Init Register: {}", this.toString());
        /** 注册queue */
        Queue rabbitQueue = registerQueue();
        /** 注册exchange */
        Exchange rabbitExchange = registerExchange();
        /** 注册binding（bind queue and exchange with queue.bindingKey）*/
        registerBinding(rabbitQueue, rabbitExchange);
    }

    /**
     * 注册queue
     *
     * @return queue实例
     */
    private Queue registerQueue() {
        /** 注册queue */
        Queue rabbitQueue = null;
        boolean existQueue = (null != queue && StringUtils.hasText(queue.getName()));
        if (existQueue) {
            rabbitQueue = new Queue(queue.getName(), queue.getDurable(), queue.getExclusive(), queue.getAutoDelete());
            //此处名字可通过拼接uuid避免重复（根据业务适当调整）
            this.configurableBeanFactory.registerSingleton("queue_".concat(queue.getName()), rabbitQueue);
        }
        return rabbitQueue;
    }

    /**
     * 注册exchange
     *
     * @return exchange实例
     */
    private Exchange registerExchange() {
        /** 注册exchange */
        Exchange rabbitExchange = null;
        boolean existExchange = (null != exchange && StringUtils.hasText(exchange.getName()));
        if (existExchange) {
            switch (exchange.getType()) {
                case ExchangeType.TOPIC:
                    rabbitExchange = new TopicExchange(exchange.getName(), exchange.getDurable(), exchange.getAutoDelete());
                    break;
                case ExchangeType.DIRECT:
                    rabbitExchange = new DirectExchange(exchange.getName(), exchange.getDurable(), exchange.getAutoDelete());
                    break;
                case ExchangeType.FANOUT:
                    rabbitExchange = new FanoutExchange(exchange.getName(), exchange.getDurable(), exchange.getAutoDelete());
                    break;
            }
            //此处名字可通过拼接uuid避免重复（根据业务适当调整）
            this.configurableBeanFactory.registerSingleton("exchange_".concat(exchange.getName()), rabbitExchange);
        }
        return rabbitExchange;
    }

    /**
     * 组册Binding（bind queue and exchange with queue.bindingKey）
     *
     * @param rabbitQueue
     * @param rabbitExchange
     * @return
     */
    private Binding registerBinding(Queue rabbitQueue, Exchange rabbitExchange) {
        /** queue绑定exchange */
        Binding rabbitBinding = null;
        boolean existBindingKey = (null != rabbitQueue && null != rabbitExchange && StringUtils.hasText(queue.getBindingKey()));
        if (existBindingKey) {
            switch (exchange.getType()) {
                case ExchangeType.TOPIC:
                    rabbitBinding = BindingBuilder.bind(rabbitQueue).to((TopicExchange) rabbitExchange).with(queue.getBindingKey());
                    break;
                case ExchangeType.DIRECT:
                    rabbitBinding = BindingBuilder.bind(rabbitQueue).to((DirectExchange) rabbitExchange).with(queue.getBindingKey());
                    break;
                case ExchangeType.FANOUT:
                    rabbitBinding = BindingBuilder.bind(rabbitQueue).to((FanoutExchange) rabbitExchange);
                    break;
            }
            //此处名字可通过拼接uuid避免重复（根据业务适当调整）
            this.configurableBeanFactory.registerSingleton("binding_".concat(queue.getBindingKey()), rabbitBinding);
        }
        return rabbitBinding;
    }

    public String getEventName() {
        return eventName;
    }

    public void setEventName(String eventName) {
        this.eventName = eventName;
    }

    public ExchangeProps getExchange() {
        return exchange;
    }

    public void setExchange(ExchangeProps exchange) {
        this.exchange = exchange;
    }

    public QueueProps getQueue() {
        return queue;
    }

    public void setQueue(QueueProps queue) {
        this.queue = queue;
    }

    /**
     * exchange - 属性
     */
    public static class ExchangeProps {
        /**
         * exchange名称
         */
        private String name;
        /**
         * exchange类型（topic|direct|fanout）
         */
        private String type = "topic";
        /**
         * routingKey
         */
        private String routingKey;
        /**
         * 是否持久化
         */
        private boolean durable = true;
        /**
         * 当所有消费客户端连接断开后，是否自动删除队列
         */
        private boolean autoDelete = false;


        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getRoutingKey() {
            return routingKey;
        }

        public void setRoutingKey(String routingKey) {
            this.routingKey = routingKey;
        }

        public boolean getDurable() {
            return durable;
        }

        public void setDurable(boolean durable) {
            this.durable = durable;
        }

        public boolean getAutoDelete() {
            return autoDelete;
        }

        public void setAutoDelete(boolean autoDelete) {
            this.autoDelete = autoDelete;
        }

        @Override
        public String toString() {
            return "Exchange{" +
                    "name='" + name + '\'' +
                    ", type='" + type + '\'' +
                    ", routingKey='" + routingKey + '\'' +
                    ", durable=" + durable +
                    ", autoDelete=" + autoDelete +
                    '}';
        }
    }


    /**
     * queue - 属性
     */
    public static class QueueProps {
        /**
         * 队列名称
         */
        private String name;
        /**
         * bindingKey
         */
        private String bindingKey;
        /**
         * 是否持久化
         */
        private boolean durable = true;
        /**
         * 仅创建者可以使用的私有队列，断开后自动删除是否持久化
         */
        private boolean exclusive = false;
        /**
         * 当所有消费客户端连接断开后，是否自动删除队列
         */
        private boolean autoDelete = false;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getBindingKey() {
            return bindingKey;
        }

        public void setBindingKey(String bindingKey) {
            this.bindingKey = bindingKey;
        }

        public boolean getDurable() {
            return durable;
        }

        public void setDurable(boolean durable) {
            this.durable = durable;
        }

        public boolean getExclusive() {
            return exclusive;
        }

        public void setExclusive(boolean exclusive) {
            this.exclusive = exclusive;
        }

        public boolean getAutoDelete() {
            return autoDelete;
        }

        public void setAutoDelete(boolean autoDelete) {
            this.autoDelete = autoDelete;
        }

        @Override
        public String toString() {
            return "Queue{" +
                    "name='" + name + '\'' +
                    ", bindingKey='" + bindingKey + '\'' +
                    ", durable=" + durable +
                    ", exclusive=" + exclusive +
                    ", autoDelete=" + autoDelete +
                    '}';
        }
    }

    /**
     * Exchange 类型
     */
    public static class ExchangeType {
        /**
         * topic
         */
        public static final String TOPIC = "topic";
        /**
         * direct
         */
        public static final String DIRECT = "direct";
        /**
         * fanout
         */
        public static final String FANOUT = "fanout";
    }

    @Override
    public String toString() {
        return "eventName: " + eventName + ", " + exchange +", " + queue;
    }
}
